<template>
    <transition
            name="el-zoom-in-top"
            @after-leave="$emit('dodestroy')">
        <div
                v-show="visible"
                class="el-time-range-picker el-picker-panel el-popper"
                :class="popperClass">
            <div class="el-time-range-picker__content">
                <div class="el-time-range-picker__cell">
                    <div class="el-time-range-picker__header">{{ t('el.datepicker.startTime') }}</div>
                    <div
                            :class="{ 'has-seconds': showSeconds, 'is-arrow': arrowControl }"
                            class="el-time-range-picker__body el-time-panel__content">
                        <time-spinner
                                ref="minSpinner"
                                :show-seconds="showSeconds"
                                :am-pm-mode="amPmMode"
                                @change="handleMinChange"
                                :arrow-control="arrowControl"
                                @select-range="setMinSelectionRange"
                                :date="minDate">
                        </time-spinner>
                    </div>
                </div>
                <div class="el-time-range-picker__cell">
                    <div class="el-time-range-picker__header">{{ t('el.datepicker.endTime') }}</div>
                    <div
                            :class="{ 'has-seconds': showSeconds, 'is-arrow': arrowControl }"
                            class="el-time-range-picker__body el-time-panel__content">
                        <time-spinner
                                ref="maxSpinner"
                                :show-seconds="showSeconds"
                                :am-pm-mode="amPmMode"
                                @change="handleMaxChange"
                                :arrow-control="arrowControl"
                                @select-range="setMaxSelectionRange"
                                :date="maxDate">
                        </time-spinner>
                    </div>
                </div>
            </div>
            <div class="el-time-panel__footer">
                <button
                        type="button"
                        class="el-time-panel__btn cancel"
                        @click="handleCancel()">{{ t('el.datepicker.cancel') }}
                </button>
                <button
                        type="button"
                        class="el-time-panel__btn confirm"
                        @click="handleConfirm()"
                        :disabled="btnDisabled">{{ t('el.datepicker.confirm') }}
                </button>
            </div>
        </div>
    </transition>
</template>

<script type="text/babel">
    import {
        parseDate,
        limitTimeRange,
        modifyDate,
        clearMilliseconds,
        timeWithinRange
    } from 'element-ui/src/utils/date-util';
    import Locale from 'element-ui/src/mixins/locale';
    import TimeSpinner from '../basic/time-spinner';

    const MIN_TIME = parseDate('00:00:00', 'HH:mm:ss');
    const MAX_TIME = parseDate('23:59:59', 'HH:mm:ss');

    const minTimeOfDay = function (date) {
        return modifyDate(MIN_TIME, date.getFullYear(), date.getMonth(), date.getDate());
    };

    const maxTimeOfDay = function (date) {
        return modifyDate(MAX_TIME, date.getFullYear(), date.getMonth(), date.getDate());
    };

    // increase time by amount of milliseconds, but within the range of day
    const advanceTime = function (date, amount) {
        return new Date(Math.min(date.getTime() + amount, maxTimeOfDay(date).getTime()));
    };

    export default {
        mixins: [Locale],

        components: {TimeSpinner},

        computed: {
            showSeconds() {
                return (this.format || '').indexOf('ss') !== -1;
            },

            offset() {
                return this.showSeconds ? 11 : 8;
            },

            spinner() {
                return this.selectionRange[0] < this.offset ? this.$refs.minSpinner : this.$refs.maxSpinner;
            },

            btnDisabled() {
                return this.minDate.getTime() > this.maxDate.getTime();
            },
            amPmMode() {
                if ((this.format || '').indexOf('A') !== -1) return 'A';
                if ((this.format || '').indexOf('a') !== -1) return 'a';
                return '';
            }
        },

        data() {
            return {
                popperClass: '',
                minDate: new Date(),
                maxDate: new Date(),
                value: [],
                oldValue: [new Date(), new Date()],
                defaultValue: null,
                format: 'HH:mm:ss',
                visible: false,
                selectionRange: [0, 2],
                arrowControl: false
            };
        },

        watch: {
            value(value) {
                if (Array.isArray(value)) {
                    this.minDate = new Date(value[0]);
                    this.maxDate = new Date(value[1]);
                } else {
                    if (Array.isArray(this.defaultValue)) {
                        this.minDate = new Date(this.defaultValue[0]);
                        this.maxDate = new Date(this.defaultValue[1]);
                    } else if (this.defaultValue) {
                        this.minDate = new Date(this.defaultValue);
                        this.maxDate = advanceTime(new Date(this.defaultValue), 60 * 60 * 1000);
                    } else {
                        this.minDate = new Date();
                        this.maxDate = advanceTime(new Date(), 60 * 60 * 1000);
                    }
                }
            },

            visible(val) {
                if (val) {
                    this.oldValue = this.value;
                    this.$nextTick(() => this.$refs.minSpinner.emitSelectRange('hours'));
                }
            }
        },

        methods: {
            handleClear() {
                this.$emit('pick', null);
            },

            handleCancel() {
                this.$emit('pick', this.oldValue);
            },

            handleMinChange(date) {
                this.minDate = clearMilliseconds(date);
                this.handleChange();
            },

            handleMaxChange(date) {
                this.maxDate = clearMilliseconds(date);
                this.handleChange();
            },

            handleChange() {
                if (this.isValidValue([this.minDate, this.maxDate])) {
                    this.$refs.minSpinner.selectableRange = [[minTimeOfDay(this.minDate), this.maxDate]];
                    this.$refs.maxSpinner.selectableRange = [[this.minDate, maxTimeOfDay(this.maxDate)]];
                    this.$emit('pick', [this.minDate, this.maxDate], true);
                }
            },

            setMinSelectionRange(start, end) {
                this.$emit('select-range', start, end, 'min');
                this.selectionRange = [start, end];
            },

            setMaxSelectionRange(start, end) {
                this.$emit('select-range', start, end, 'max');
                this.selectionRange = [start + this.offset, end + this.offset];
            },

            handleConfirm(visible = false) {
                const minSelectableRange = this.$refs.minSpinner.selectableRange;
                const maxSelectableRange = this.$refs.maxSpinner.selectableRange;

                this.minDate = limitTimeRange(this.minDate, minSelectableRange, this.format);
                this.maxDate = limitTimeRange(this.maxDate, maxSelectableRange, this.format);

                this.$emit('pick', [this.minDate, this.maxDate], visible);
            },

            adjustSpinners() {
                this.$refs.minSpinner.adjustSpinners();
                this.$refs.maxSpinner.adjustSpinners();
            },

            changeSelectionRange(step) {
                const list = this.showSeconds ? [0, 3, 6, 11, 14, 17] : [0, 3, 8, 11];
                const mapping = ['hours', 'minutes'].concat(this.showSeconds ? ['seconds'] : []);
                const index = list.indexOf(this.selectionRange[0]);
                const next = (index + step + list.length) % list.length;
                const half = list.length / 2;
                if (next < half) {
                    this.$refs.minSpinner.emitSelectRange(mapping[next]);
                } else {
                    this.$refs.maxSpinner.emitSelectRange(mapping[next - half]);
                }
            },

            isValidValue(date) {
                return Array.isArray(date) &&
                    timeWithinRange(this.minDate, this.$refs.minSpinner.selectableRange) &&
                    timeWithinRange(this.maxDate, this.$refs.maxSpinner.selectableRange);
            },

            handleKeydown(event) {
                const keyCode = event.keyCode;
                const mapping = {38: -1, 40: 1, 37: -1, 39: 1};

                // Left or Right
                if (keyCode === 37 || keyCode === 39) {
                    const step = mapping[keyCode];
                    this.changeSelectionRange(step);
                    event.preventDefault();
                    return;
                }

                // Up or Down
                if (keyCode === 38 || keyCode === 40) {
                    const step = mapping[keyCode];
                    this.spinner.scrollDown(step);
                    event.preventDefault();
                    return;
                }
            }
        }
    };
</script>
